///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2007-2009 ILOG, S.A.
//  All Rights Reserved.
//  The following is ILOG Source Code.  No modifications may be made to the  
//  ILOG Source Code.  Any usage of the ILOG Source Code is subject to 
//  the terms and conditions of the ILOG End User License Agreement   
//  applicable to this ILOG software product.
//
///////////////////////////////////////////////////////////////////////////////

package ilog.gauges.rectangular.renderers
{
  import flash.geom.Rectangle;
  
  import ilog.gauges.gaugesClasses.rectangular.RectangularScaleRelatedRenderer;
  import ilog.utils.CSSUtil;
  
  import mx.graphics.IFill;
  import mx.graphics.IStroke;
  import mx.graphics.LinearGradient;
  import mx.graphics.SolidColor;
  import mx.styles.CSSStyleDeclaration;
    
  /**
   * Specifies the stroke for the track.
   * This style must be an instance of an implementation of the <code>IStroke</code> interface.  
   * 
   * @default undefined
   */
  [Style(name="stroke", type="mx.graphics.IStroke", inherit="no")]

  /**
   * Specifies the color of the track. This style property is only used if <code>gradientEntries</code>
   * is null, empty, or contains only one entry.
   *
   * @default 0xFF0000
   */
  [Style(name="rendererColor", type="uint", format="Color", inherit="no")]

  /**
   * Specifies the alpha of the track. This style property is only used if <code>gradientEntries</code>
   * is null, empty, or contains only one entry.
   *
   * @default 1.0
   */
  [Style(name="rendererAlpha", type="Number", inherit="no")]   


  /**
   * A <code>RectangularTrackRenderer</code> is used to represent a range of 
   * values on a scale.
   * The geometry of the track is determined by <code>startThickness</code> and 
   * <code>endThickness</code> which are expressed relative to the renderer height
   * if the gauge is horizontal or width if the gauge is vertical. If 
   * <code>startThickness</code> is equal to <code>endThickness</code>, the track 
   * has a constant thickness. However, if, for example, <code>endThickness</code> is 
   * greater than <code>startThickness</code>, the thickness of the track will 
   * increase when going from <code>minimum</code> to <code>maximum</code> value.  
   * <p>
   * The track is filled with the <code>gradientEntries</code> array if it is 
   * defined. Otherwise, <code>rendererColor</code> and <code>rendererAlpha</code> style 
   * properties are used. 
   * </p>
   * 
   * @mxml
   *  
   * <p>The <code>&lt;ilog:RectangularTrackRenderer&gt;</code> tag inherits all the properties
   * of its parent classes and adds the following properties:</p>
   *  
   * <pre>
   * &lt;ilog:CircularTrackRenderer
   *   <strong>Properties</strong>
   *   placement="trailing|leading" 
   *   startThickness="<i>0%</i>"
   *   endThickness="<i>100%</i>"
   *   gradientEntries="<i>No default</i>" 
   *    
   *   <strong>Styles</strong>
   *   rendererColor="0xFF0000"
   *   rendererAlpha="1.0"
   *   stroke="<i>No default</i>" 
   * /&gt;
   * </pre>
   *    
   * @includeExample examples/RectangularTrackRendererExample.mxml -noswf
   */    
  public class RectangularTrackRenderer extends RectangularScaleRelatedRenderer
  {
    /**
     *  @private
     */
    private static var stylesInited:Boolean = initStyles(); 

    /**
     *  @private
     */
    private static function initStyles():Boolean
    {
      var style:CSSStyleDeclaration =
        CSSUtil.createSelector("RectangularTrackRenderer");   
            
      style.defaultFactory = function():void {
        this.renderAlpha = 1;
        this.rendererColor = 0xff0000;
      }

      return true;
    }    
    
    private var _linearGradient:LinearGradient = null;

    /**
     * Constructor.
     */
    public function RectangularTrackRenderer()
    {     
    }
    
    private var _minimum:Object;
    
    [Bindable]
    [Inspectable(category="Gauge")]    

    /**
     * The value at which the track starts. It serves to compute the start 
     * position of the track by using the scale attached to the track.
     * 
     * @default null
     */    
    public function get minimum():Object
    {
      return _minimum;
    }

    /**
     * @private
     */
    public function set minimum(value:Object):void
    {
      _minimum = value;      
      invalidateDisplayList();
    }
    
    private var _maximum:Object;
    
    [Bindable]
    [Inspectable(category="Gauge")]    
    
    /**
     * The value at which the track ends. It serves to compute the end 
     * position of the track by using the scale attached to the track.
     * 
     * @default null
     */      
    public function get maximum():Object
    {
      return _maximum;
    }

    /**
     * @private
     */
    public function set maximum(value:Object):void
    {
      _maximum = value;      
      invalidateDisplayList();
    }
            
    private var _gradientEntries:Array;

    [Inspectable(category="Gauge")]    

    /**
     * An array of <code>mx.graphics.GradientEntry</code> to fill the track. 
     * If this array is not specified, the style properties
     * <code>rendererColor</code> and <code>rendererAlpha</code>
     * are used.
     * 
     * @default null
     *      
     * @see mx.graphics.GradientEntry 
     */    
    public function get gradientEntries():Array
    {
      return _gradientEntries;
    }
    
    /**
     * @private
     */
    public function set gradientEntries(value:Array):void
    {
      _gradientEntries = value;
      invalidateDisplayList();
    }    

    private function getStartThickness():Number
    {
      var length:Number = (gauge.direction == "horizontal") ? unscaledHeight : unscaledWidth;
      return isNaN(startThickness) ? length * percentStartThickness/100 : startThickness;
    }
    
    private function getEndThickness():Number
    {
      var length:Number = (gauge.direction == "horizontal") ? unscaledHeight : unscaledWidth;      
      return isNaN(endThickness) ? length * percentEndThickness/100 : endThickness;
    }
 
    private var _startThickness:Number = NaN;

    [Bindable("startThicknessChanged")]
    [Inspectable(category="Gauge")]
    [PercentProxy("percentStartThickness")]

    /**
     * Number that specifies the starting thickness of the track, in pixels. 
     *
     * <p><strong>Note:</strong> You can specify a percentage value in the MXML
     * <code>startThickness</code> attribute, such as <code>startThickness="5%"</code>,
     * but you cannot use a percentage value in the <code>startThickness</code>
     * property in ActionScript.
     * Use the <code>percentStartThickness</code> property instead.</p>
     * 
     * @default NaN
     */
    public function get startThickness():Number
    {
      return _startThickness;
    }

    /**
     * @private
     */
    public function set startThickness(value:Number):void
    {
      _startThickness = value;
      if (!isNaN(_startThickness))
        _percentStartThickness = NaN;
      invalidateDisplayList();      
    }
        
    private var _percentStartThickness:Number = 0;

    [Inspectable(environment="none")]
    
    /**
     * Number that specifies the starting thickness of the track as a percentage
     * of the track height, if the gauge is horizontal, or of the track width, if the gauge
     * is vertical.
     * 
     * Allowed values are 0-100.
     * 
     * @default 0
     */
    public function get percentStartThickness():Number
    {
      return _percentStartThickness;
    }
 
    /**
     * @private
     */
    public function set percentStartThickness(value:Number):void
    {
      _percentStartThickness = value;
      if (!isNaN(_percentStartThickness)) 
        _startThickness = NaN;
      invalidateDisplayList();
    }

    private var _endThickness:Number = NaN;

    [Bindable("endThicknessChanged")]
    [Inspectable(category="Gauge")]
    [PercentProxy("percentEndThickness")]

    /**
     * Number that specifies the ending thickness of the track, in pixels. 
     *
     * <p><strong>Note:</strong> You can specify a percentage value in the MXML
     * <code>endThickness</code> attribute, such as <code>endThickness="50%"</code>,
     * but you cannot use a percentage value in the <code>endThickness</code>
     * property in ActionScript.
     * Use the <code>percentEndThickness</code> property instead.</p>
     * 
     * @default NaN
     */
    public function get endThickness():Number
    {
      return _endThickness;
    }

    /**
     * @private
     */
    public function set endThickness(value:Number):void
    {
      _endThickness = value;
      if (!isNaN(_endThickness))
        _percentEndThickness = NaN;
      invalidateDisplayList();      
    }
        
    private var _percentEndThickness:Number = 100;

    [Inspectable(environment="none")]
    
    /**
     * Number that specifies the ending thickness of the track as a percentage
     * of the track height, if the gauge is horizontal, or of the track width, if the gauge
     * is vertical.
     * 
     * Allowed values are 0-100.
     * 
     * @default 100
     */
    public function get percentEndThickness():Number
    {
      return _percentEndThickness;
    }

    /**
     * @private
     */
    public function set percentEndThickness(value:Number):void
    {
      _percentEndThickness = value;
      if (!isNaN(_percentEndThickness))
        _endThickness = NaN;
      invalidateDisplayList();
    }    
    
    private var _placement:String = "trailing"; 
    
    [Inspectable(category="Gauge", defaultValue="trailing", enumeration="leading,trailing", type="String")]
   
    /**
     * Specifies where to draw the track. Options are:
     * <ul>    
     *   <li><code>"trailing"</code> (default) -
     *   Draw the track at the bottom if the gauge is horizontal,
     *   to the right if the gauge is vertical.</li>
     *
     *   <li><code>"leading"</code> -
     *   Draw the track at the top if gauge is horizontal,
     *   to the left if the gauge is vertical.</li>
     * </ul>
     * 
     * @default "trailing"
     */  
    public function get placement():String
    {
      return _placement;
    }
   
    /**
     * @private
     */   
    public function set placement(value:String):void
    {
      _placement = value;
      invalidateDisplayList();
    }
    
    /**
     * Draws the track.
     *
     * @param unscaledWidth Specifies the width, in pixels, of the component
     * in the component's coordinates, regardless of the value of the
     * <code>scaleX</code> property of the component.
     *
     * @param unscaledHeight Specifies the height, in pixels, of the component
     * in the component's coordinates, regardless of the value of the
     * <code>scaleY</code> property of the component. 
     */     
    protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
      // call super in case we are the master to update the scale     
      super.updateDisplayList(unscaledWidth, unscaledHeight);
            
      var startPos:Number = scale.positionForValue(minimum);
      var endPos:Number = scale.positionForValue(maximum);
      graphics.clear();
      var fill:IFill;
      if (gradientEntries != null) {
        if (_linearGradient == null)
          _linearGradient = new LinearGradient();
        _linearGradient.entries = gradientEntries;
        if (gauge.direction == "horizontal")
          _linearGradient.angle = 0;
        else
          _linearGradient.angle = -90;
        fill = _linearGradient; 
      } else {
        fill = new SolidColor(getStyle("rendererColor"), getStyle("renderAlpha"));
      }
      var stroke:IStroke = getStyle("stroke");
      if (stroke != null) {
        stroke.apply(graphics);
      }
      var leading:Boolean = placement == "leading";
      var level1:Number, level2:Number, level3:Number;
      if (gauge.direction == "horizontal") {
        level1 = leading ? getStartThickness() : unscaledHeight - getStartThickness();
        level2 = leading ? getEndThickness() : unscaledHeight - getEndThickness();
        level3 = leading ? 0 : unscaledHeight;
        fill.begin(graphics, new Rectangle(startPos, 0, endPos - startPos, unscaledHeight));
        graphics.moveTo(startPos, level1);
        graphics.lineTo(endPos, level2);
        graphics.lineTo(endPos, level3);
        graphics.lineTo(startPos, level3);
        graphics.lineTo(startPos, level1);
      } else {
        level1 = leading ? getStartThickness() : unscaledWidth - getStartThickness();
        level2 = leading ? getEndThickness() : unscaledWidth - getEndThickness();
        level3 = leading ? 0 : unscaledWidth;        
        fill.begin(graphics, new Rectangle(0, unscaledHeight - endPos, unscaledWidth, endPos - startPos));
        graphics.moveTo(level1, unscaledHeight - startPos);
        graphics.lineTo(level2, unscaledHeight - endPos);
        graphics.lineTo(level3, unscaledHeight - endPos);
        graphics.lineTo(level3, unscaledHeight - startPos);
        graphics.lineTo(level1, unscaledHeight - startPos);              
      }
      fill.end(graphics);
    }
  }
}
